 /* 
 * Mesiab Labs Adobe Air AJAX component
 *
 * Author: 	Kevin Mesiab
 * Date:	May 15, 2009
 * Version: .01 Beta
 *
 * Provides a simple interface for performing 
 * ajax operations using the built in adobe air
 * class members.  Also provides status access
 * via events.
 * 
 */
$.web = new web();

/* Perform an ajax call.
 *
 * Paramters for @o
 * 
 * authenticate
 * username
 * password
 * responsetype { json, xml, text, binary }
 * useragent
 * cache
 * method
 * manageCookies
 * allowRedirect
 * 
 */
$.web.ajax = function(o) {
	
	// Create a web object
	var w = new web();
	
	// Assign local properties
	$.extend( w, o);
	
	// Fire the call
	if (w.method.toLowerCase() == 'get') {
		
		w.get(o.url, o.data, o.progress, o.complete);
		
	} else {
		
		w.post(o.url, o.data, o.progress, o.complete);
		
	}
}
			
 function web() {
 		
		// Initiate an HTTP Post
		this.post = function(url, data, progress, complete) {
			init(url, progress, complete);
			var r = createRequest('POST', data)
			loader.load(r);
		}
		
		// Initiate an HTTP Get
		this.get = function(url, data, progress, complete) {
			init(url, progress, complete);
			var r = createRequest('GET', data)
			loader.load(r);
		}
	
		this.upload = function(requestUrl, file, name, data, p, c) {
					
			url = requestUrl;
			progress = p;
			complete = c;			
			file.addEventListener( air.ProgressEvent.PROGRESS, onProgress);
			file.addEventListener( air.DataEvent.UPLOAD_COMPLETE_DATA, onComplete);
			
			var r = createRequest('POST', data);
			file.upload(r, name, false);
		
		}
		
		/* Public Members */
		
		// True if the user/pass should be hashed and
		// sent as an authorization request
		this.authenticate = false;
		
		// The username to use if authenticate is set to true
		this.username;
		
		// The password to use if authenticate is set to true
		this.password;
		
		// Determines how the response data is processed
		// either returned as pure text, a Jquery xml document,
		// json or binary data
		this.responsetype = 'json';
		
		// Allow the request object to automatically follow HTTP 
		// redirect responses.
		this.allowRedirect = true;
		
		// Manage cookies internally.
		this.manageCookies = false;
		
		// Use caching to speed up responses
		this.cache = true;
		
		// The useragent string to present to remote servers
		this.useragent = 'Hummingbird/2.0';
		
		
		
		/* Private Members */
		
		var self 	 = this;
		var url 	 = undefined;
		var complete = undefined;
		var progress = undefined;
		var loader 	 = new air.URLLoader();
		
		
		/* Private Functions */
		
		// Sets internal variable state and wires up the 
		// event handlers.  Also sets binary encoding if specified		
		var init = function(requestUrl, progressCallback, completCallback) {
			
			url = requestUrl;
			complete = completCallback;
			progress = progressCallback;
			
			watchLoader();
			
			if( self.responsetype && self.responsetype.toLowerCase() == 'binary' )
				loader.dataFormat = air.URLLoaderDataFormat.BINARY;
		}
		
		// Creates a default URLRequest object
		var createRequest = function(method, data) {
			
			var r = new air.URLRequest(url);
			
			if( data && !$.isArray(data) && !$.isempty(data) ) {
				
				r.data = encodeParams(data);
				
			} else {
				
				if( !$.isempty(data) ) r.data = data;
				
			}
			
			r.method = (method) ? method : air.URLRequestMethod.GET;
			r.cacheResponse = self.cache;
			r.useCache = self.cache;
			r.userAgent = self.useragent;
			r.manageCookies = self.manageCookies;
			r.followRedirects = self.allowRedirect;
			
			if( self.authenticate && self.username && self.password ) {
				var hash = $.base64Encode(self.username + ":" + self.password);
				r.requestHeaders = [new air.URLRequestHeader('Authorization', 'Basic ' + hash )];
			}
			
			return r;
		}
		
		// Binds the local event handlers to the loader
		var watchLoader = function() {
			if( !loader ) throw 'No loader to watch';
			
			loader.addEventListener(air.Event.COMPLETE, onComplete);
			loader.addEventListener(air.SecurityErrorEvent.SECURITY_ERROR, onComplete);
			loader.addEventListener(air.IOErrorEvent.IO_ERROR, onComplete);
			loader.addEventListener(air.ProgressEvent.PROGRESS, onProgress);
		}
		
		// Unbinds the local event handlers from the loader
		var unwatchLoader = function() {
			if( !loader ) throw 'No loader to unwatch'; 
			loader.removeEventListener(air.Event.COMPLETE, onComplete);
			loader.removeEventListener(air.SecurityErrorEvent.SECURITY_ERROR, onComplete);
			loader.removeEventListener(air.IOErrorEvent.IO_ERROR, onComplete);
			loader.removeEventListener(air.ProgressEvent.PROGRESS, onProgress);
		}
		
		// Converts an array of { name:'', value:''} into a querystring 
		var encodeParams = function(data) {
			
			var q = '';
			for( var i in data ) {
				
				var v = data[i];
				var d = escape(v);
				
				q += i + '=' + d + '&';			
			}
			
			q = q.substring(0, q.length-1);
			return new air.URLVariables(q);
		}
	
		var onComplete = function(e) {
			
			// Unwire the event handlers
			unwatchLoader();
			
			// If we cant fire a callback, quit
			if( !complete || typeof complete != 'function' ) return;
				
			// If the loader is null, something went wrong
			if( !loader ) {
				complete({ 
					response: null,
					success	: false, 
					status	: 'Internal Error',
					error	: 'Loader was null'
				});
				return;
			}
			
			var response = {};
			// If there is data attempt to encode it
			if (loader.bytesLoaded > 0 || e.data) {
				
				var data = (e.data) ? e.data : loader.data;
				
				try {
					// Process the response data
					switch (self.responsetype) {
					
						case 'json':
							response = eval('(' + data + ')');
							break;
							
						case 'xml':
							response = $(data);
							break;
							
						default:
							response = data;
							break;
					}
				} 
				catch (ex) {
					
					var response = null;
					var satus = 'Response Encoding Error';
					if( data.match( /Server Error 503/gim ) ) {
						status = '503 Server Error';
						response = data;
						ex = 'Twitter Fail Whale';
					}
					
					complete({
						response: response,
						success: false,
						status: status,
						error: ex
					});
					return
				}
			}
			
			if( e.type == 'ioError' ) {
				
				var errormsg = (response.error) ? response.error : e.text
							
				complete({ 
					response: response,
					success	: false, 
					status	: e.text,
					error	: errormsg
				});
				return;
			}
			
			complete({ 
				response: response,
				success	: true, 
				status	: 'success',
				error	: null
			});
		}
		
		var onProgress = function(e) {
			
			// If we cant fire a callback, quit
			if( !progress || typeof progress != 'function' ) return;
			
			progress({
				bytesLoaded : e.bytesLoaded,
				bytesTotal  : e.bytesTotal,
				url			: url
			});
			
		}
 	
 }
